import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

public class MySyntaxAnalyzer implements SyntaxAnalyzer {

	//public MySemanticAnalyzer semantic;
    MyLexicalAnalyzer lexer = new MyLexicalAnalyzer();
    MySemanticAnalyzer semantic = new MySemanticAnalyzer();
    //semantic.html();

	
	@Override
	//this method calls everything that may need to be looked at. if these tags are not present, the document fails and cannot be 
	//compiled and outputted. this is the most important requirement.at the end of this when the end document is found then it 
	//goes to the semantic analyzer.
	public void document() throws IOException, CompilerException {
		lexer.getNextToken();
		if (!(Compiler.token.equalsIgnoreCase("#START DOCUMENT"))) {
			String message = "A start document association was expected when a " + Compiler.token + "was found!";
			throw new CompilerException(message);
		}
		else {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			defineglobal();
			head();
			body();
			if (!(Compiler.token.equalsIgnoreCase("#END DOCUMENT"))) {
				String message = "A start document association was expected when a " + Compiler.token + "was found!";
				throw new CompilerException(message);
			} 
			else {
				Compiler.parsetree.add(Compiler.token);
			semantic.html();
			}
		}
	}
		
	//this is my global define method. when a name or value is being defined right after the document tag, then it is global
	//and can be called anywhere within the document.
	public void defineglobal() throws IOException, CompilerException {
		if (Compiler.token.equalsIgnoreCase("#START DEFINE")) {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			if (Compiler.token.equalsIgnoreCase("#NAME")) {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
				Compiler.variablename = Compiler.token;
				text(Compiler.variablename);
				lexer.getNextToken();
				if (Compiler.token.equalsIgnoreCase("#VALUE")) {
					Compiler.parsetree.add(Compiler.token);
					lexer.getNextToken();
					Compiler.valueofvalue = Compiler.token;
					text(Compiler.valueofvalue);
					lexer.getNextToken();
					if (Compiler.token.equalsIgnoreCase("#END DEFINE")) {
						Compiler.parsetree.add(Compiler.token);
						lexer.getNextToken();
						defineglobal();
					} else {
						String message = "A start document association was expected when a "
								+ Compiler.token + "was found!";
						throw new CompilerException(message);
					}
				} else {
					String message = "A start document association was expected when a "
							+ Compiler.token + "was found!";
					throw new CompilerException(message);
				}
			} else {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			}
		}
	}

	@Override
	//this tag is optional, so when it is called, it should either look for the tag and if it doesnt see it, it can go back to the
	//previous method and do what is left.
	public void head() throws IOException, CompilerException {
		if (Compiler.token.equalsIgnoreCase("#START HEAD")) {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			title();
			if (Compiler.token.equalsIgnoreCase("#END HEAD")) {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
			} else {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			}
		}
	}

	@Override
	public void title() throws CompilerException, IOException {
		// get text and add to tree. get next token and make sure that it has end title.
		if (!(Compiler.token.equalsIgnoreCase("#START TITLE"))) {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			text(Compiler.token);
			lexer.getNextToken();
			if (Compiler.token.equalsIgnoreCase("#END TITLE")) {
				Compiler.parsetree.add(Compiler.token);
			} else {
				// return to pervious method
			}
		}
	}

	public void body() throws CompilerException, IOException {
		innertext(); // does this work right?
		body();
		if (Compiler.token.equalsIgnoreCase("#START PARAGRAPH")) {
			paragraph();
			body();
		} else if (Compiler.token.equalsIgnoreCase("#START BOLD")) {
			bold();
			body();
		} else if (Compiler.token.equalsIgnoreCase("#START ITALICS")) {
			italics();
			body();
		} else if (Compiler.token.equalsIgnoreCase("#START LIST")) {
			list();
			body();
		} else {
		}
	}

	@Override
	//checks for a paragraph tag and adds it if it sees it. this is not an optional method when it is called, therefore it must 
	//be the tag or else an error is produced and it exits the program.
	public void paragraph() throws CompilerException, IOException {
		if (!(Compiler.token.equalsIgnoreCase("#START PARAGRAPH"))) {
			String message = "A start document association was expected when a "
					+ Compiler.token + "was found!";
			throw new CompilerException(message);
		} else {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			define();
			innerparagraph();
			if (Compiler.token.equalsIgnoreCase("#END PARAGRAPH")) {
				Compiler.parsetree.add(Compiler.token);
			} else {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			}
		}
	}

	@Override
	//checks for a bold tag and adds it if it sees it. this is not an optional method when it is called, therefore it must 
	//be the tag or else an error is produced and it exits the program.
	public void bold() throws CompilerException, IOException {
		if (!(Compiler.token.equalsIgnoreCase("#START BOLD"))) {
			String message = "A start document association was expected when a "
					+ Compiler.token + "was found!";
			throw new CompilerException(message);
		} else {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			define();
			innertext();
			if (!(Compiler.token.equalsIgnoreCase("#END BOLD"))) {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			} else {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
			}
		}
	}

	@Override
	//checks for a italics tag and adds it if it sees it. this is not an optional method when it is called, therefore it must 
	//be the tag or else an error is produced and it exits the program.
	public void italics() throws CompilerException, IOException {
		if (!(Compiler.token.equalsIgnoreCase("#START ITALICS"))) {
			String message = "A start document association was expected when a "
					+ Compiler.token + "was found!";
			throw new CompilerException(message);
		} else {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			define();
			innertext();
			if (!(Compiler.token.equalsIgnoreCase("#END ITALICS"))) {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			} else {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
			}
		}
	}

	@Override
	//checks for a list tag and adds it if it sees it.
	public void list() throws CompilerException, IOException {
		if (!(Compiler.token.equalsIgnoreCase("#START LIST"))) {
			String message = "A start document association was expected when a "
					+ Compiler.token + "was found!";
			throw new CompilerException(message);
		} else {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			if (!(Compiler.token.equalsIgnoreCase("#START ITEM"))) {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			} else {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
				define();
				innerlist();
				if (!(Compiler.token.equalsIgnoreCase("#END ITEM"))) {
					String message = "A start document association was expected when a "
							+ Compiler.token + "was found!";
					throw new CompilerException(message);
				} else {
					Compiler.parsetree.add(Compiler.token);
					lexer.getNextToken();
					listitems();
					if (!(Compiler.token.equalsIgnoreCase("#END LIST"))) {
						String message = "A start document association was expected when a "
								+ Compiler.token + "was found!";
						throw new CompilerException(message);
					} else {
						Compiler.parsetree.add(Compiler.token);
						lexer.getNextToken();
					}
				}
			}
		}
	}
	//checks for a list tag and adds it if it sees it. this is an optional method therefore it does not need an error if it does 
	//not see the tag.
	public void innerlist() throws IOException, CompilerException {
		innertext();
		innerlist();
		if (Compiler.token.equalsIgnoreCase("#START BOLD")) {
			bold();
			innerlist();
		} else if (Compiler.token.equalsIgnoreCase("#START ITALICS")) {
			italics();
			innerlist();
		} else if (Compiler.token.equalsIgnoreCase("#START LIST")) {
			list();
			innertext();
		}
	}

	@Override
	//checks for a listitems tag and adds it if it sees it. this is an optional method therefore it does not need an error if it does 
	//not see the tag.
	public void listitems() throws CompilerException, IOException {
		if (Compiler.token.equalsIgnoreCase("#START ITEM")) {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			define();
			innerlist();
			if (Compiler.token.equalsIgnoreCase("END ITEM")) {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
			} else {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			}
		}

	}

	@Override
	//checks for a audio tag and adds it if it sees it. this is an optional method therefore it does not need an error if it does 
	//not see the tag.
	public void audio() throws CompilerException, IOException {
		if (Compiler.token.equalsIgnoreCase("#START AUDIO ")) {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			text(Compiler.token);
			if (Compiler.token.equalsIgnoreCase("#END AUDIO")) {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
			} else {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			}
		}
	}

	@Override
	//checks for a video tag and adds it if it sees it. this is an optional method therefore it does not need an error if it does 
	//not see the tag.
	public void video() throws CompilerException, IOException {
		if (Compiler.token.equalsIgnoreCase("#START VIDEO")) {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			text(Compiler.token);
			if (Compiler.token.equalsIgnoreCase("#END VIDEO")) {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
			} else {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			}
		}
	}

	@Override
	//this is my local define method. when a name is definied locally and only within a paragraph or anything that is inside of 
	//a tag that is not document, it is local. I made two variables to hold the name and value that are local that can be called 
	//inside the semantic analyzer.
	public void define() throws CompilerException, IOException {
		if (Compiler.token.equalsIgnoreCase("#START DEFINE")) {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			if (Compiler.token.equalsIgnoreCase("#NAME")) {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
				String variablename = Compiler.token;
				text(variablename);
				lexer.getNextToken();
				if (Compiler.token.equalsIgnoreCase("#VALUE")) {
					Compiler.parsetree.add(Compiler.token);
					lexer.getNextToken();
					String valueofvalue = Compiler.token;
					text(valueofvalue);
					lexer.getNextToken();
					if (Compiler.token.equalsIgnoreCase("#END DEFINE")) {
						Compiler.parsetree.add(Compiler.token);
						lexer.getNextToken();
						define();
					} else {
						String message = "A start document association was expected when a "
								+ Compiler.token + "was found!";
						throw new CompilerException(message);
					}
				} else {
					String message = "A start document association was expected when a "
							+ Compiler.token + "was found!";
					throw new CompilerException(message);
				}
			} else {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			}
		}
	}

	//this method is made to use any name that is defined.
	@Override
	public void use() throws CompilerException, IOException {
		if (Compiler.token.equalsIgnoreCase("#USE")) {
			Compiler.parsetree.add(Compiler.token);
			lexer.getNextToken();
			Compiler.variableuse = Compiler.token;
			text(Compiler.variableuse);
			lexer.getNextToken();
			if (!(Compiler.token.equalsIgnoreCase("#END USE"))) {
				String message = "A start document association was expected when a "
						+ Compiler.token + "was found!";
				throw new CompilerException(message);
			} else {
				Compiler.parsetree.add(Compiler.token);
				lexer.getNextToken();
			}
		}
	}

	public void innerparagraph() throws IOException, CompilerException {
		innertext();// In the bnf, this is listed as a condition, but since it is not a tag, I thought it would be most effective
		//to check it first, since it can be empty, then it returns to the if statements. When I compiled/ran the program,
		//this recursive method was producing errors and contiues to do so. :/
		if (Compiler.token.equalsIgnoreCase("#START BOLD")) {
			bold();
			//innerparagraph();
		} else if (Compiler.token.equalsIgnoreCase("#START ITALICS")) {
			italics();
			//innerparagraph();
		} else if (Compiler.token.equalsIgnoreCase("#START LIST")) {
			list();
			//innerparagraph();
		} else if (Compiler.token.equalsIgnoreCase("#NEWLINE")) {
			newline();
			//innerparagraph();
		}
	}
	
	// This method is giving me problems. Im not sure what to do at this point.
	public void innertext() throws IOException, CompilerException {
		if (Compiler.token.equalsIgnoreCase("#START USE")) {
			use();
			//innertext();
		} else if (Compiler.token.equalsIgnoreCase("#NEWLINE")) {
			newline();
			//innertext();
		} else if (Compiler.token.equalsIgnoreCase("#START AUDIO")) {
			audio();
			//innertext();
		} else if (Compiler.token.equalsIgnoreCase("#START VIDEO")) {
			video();
			//innertext();
		} else if (!(Compiler.token.equals("#jfdsafjdas"))) {
			text(Compiler.token);
			//innertext();
		}
	}

	//if this method sees a newline tag in the correct position then it will add it to the parse tree and return to the previous method.
	public void newline() {
		if (Compiler.token.equalsIgnoreCase("#NEWLINE")) {
			Compiler.parsetree.add(Compiler.token);
			newline();
		}
	}
	//this method is to check for anything that is not a tag and add it to the parse tree. 
	public void text(String text) throws IOException, CompilerException {
		if (!(text.equalsIgnoreCase("#fdsfa"))) {
			Compiler.parsetree.add(text);
			lexer.getNextToken();
		}
	}
}
